package cn.gjxblog.nio;

import org.junit.Test;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

/**
 * DMA -> 通道channel
 * DMA :
 * channel ： 完全独立的处理器，专门处理io 不用向cpu申请调度
 * 一 、 通道 channel: 用户源节点与目标节点的连接。在javaNIO 中负责缓冲区数据的传输。
 * channel本身补偿你出数据，因此需要配合缓冲区进行传输。
 *
 * 二、 主要实现类
 *  java.nio.channels.Channel 接口
 *      |--FileChannel
 *      |--SocketChannel
 *      |--ServerSocketChannel
 *      |--DatagramChannel
 * 三 获取通道
 *  1 getChannel() 方法
 *
 *  本地IO
 *      FileInputStream/FileOutPutStream
 *      RandomAccessFile
 *  网络IO:
 *      Socket
 *      ServerSocket
 *      DatagramSocket
 *  2 JDK1.7 中的NIO.2 针对各个通道提供了静态方法open()
 *  3 在JKD1.7 中的NIO2 的Files工具类newByteChannel()
 *
 * 四、通道直接的数据传输
 *  transferFrom()
 *  transferTo()
 *
 * 五、分散（Scatter）与 聚集（Gather）
 * 分散读取（Scattering Reads） ： 将通道中的数据分散多个缓冲区
 * 聚集（Gather writers）: 将多个缓冲区中的数据聚集到channel中
 *
 * 六 字符集
 * 编码 字符串-》 字节数组
 * 解码 字节数组-》字符串
 *
 * @author gjx
 * @version v1.0
 * @Description
 * @date 2019/7/24 9:48
 */
public class TestChannel {

    //分散和聚集
    @Test
    public void test4() throws IOException {
        RandomAccessFile randomAccessFile = new RandomAccessFile("41.png","rw");

        //1 获取通道
        FileChannel channel = randomAccessFile.getChannel();

        //2 分配指定大小的缓冲区
        ByteBuffer buffer1 = ByteBuffer.allocate(100);
        ByteBuffer buffer2 = ByteBuffer.allocate(100);

        //3 分散读取
        ByteBuffer[] buffers = {buffer1,buffer2};


        for (ByteBuffer buffer : buffers) {
            buffer.flip();
        }
        channel.read(buffers);
//        System.out.println(new String(buffers[0].array(),0,buffers[0].limit()));
//        System.out.println(new String(buffers[1].array(),0,buffers[1].limit()));

        //4 聚集写入
        RandomAccessFile randomAccessFile2 = new RandomAccessFile("44.png","rw");
        FileChannel channel1 = randomAccessFile2.getChannel();
        channel1.write(buffers);

        channel1.close();
        channel.close();

    }

    //通道直接的数据传输（直接缓存区）
    @Test
    public void test3() throws IOException {
        FileChannel inChannel = FileChannel.open(Paths.get("41.png"), StandardOpenOption.READ);
        FileChannel outChannel = FileChannel.open(Paths.get("43.png"), StandardOpenOption.WRITE,StandardOpenOption.CREATE_NEW,StandardOpenOption.READ);

        inChannel.transferTo(0,inChannel.size(),outChannel);
        inChannel.close();
        outChannel.close();
    }


    //2. 使用直接缓冲区完成文件的复制（内存映射文件）
    @Test
    public void test2() throws IOException {
        long start = System.currentTimeMillis();

        FileChannel inChannel = FileChannel.open(Paths.get("41.png"), StandardOpenOption.READ);
        FileChannel outChannel = FileChannel.open(Paths.get("43.png"), StandardOpenOption.WRITE,StandardOpenOption.CREATE_NEW,StandardOpenOption.READ);

        //内存映射文件
        MappedByteBuffer inMap = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());

        MappedByteBuffer outMap = outChannel.map(FileChannel.MapMode.READ_WRITE, 0, inChannel.size());

        //直接对缓冲区进行读写操作
        byte[] bytes = new byte[inMap.limit()];
        inMap.get(bytes);
        outMap.put(bytes);

        inChannel.close();
        outChannel.close();

        long end = System.currentTimeMillis();
        System.out.println("cost time :"+(end-start));
    }

    //1. 利用通道完成文件的复制(非直接缓冲区)
    @Test
    public void test1() throws IOException {
        long start = System.currentTimeMillis();

        FileInputStream fileInputStream = new FileInputStream("41.png");
        FileOutputStream fileOutputStream = new FileOutputStream("42.png");

        //1 获取通道
        FileChannel inChannel = fileInputStream.getChannel();
        FileChannel outChannel = fileOutputStream.getChannel();

        //2 分配指定大小的缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(1024);

        //3 将管道中的数据存入缓冲区中
        while(inChannel.read(buffer)!=-1){
            // 4 将缓冲区中数据写入通道中
            buffer.flip();
            // 将缓冲区中的数据写入通道中
            outChannel.write(buffer);
            buffer.clear();//清空缓冲区
        }

        outChannel.close();
        inChannel.close();
        fileOutputStream.close();
        fileInputStream.close();

        long end = System.currentTimeMillis();
        System.out.println("cost time :"+(end-start));

    }
}
